
import com.sun.org.apache.bcel.internal.generic.IF_ICMPGE;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Tom
 */
public class VoxelProcessing {
   
    //__KONSTANTY
    int SEGMENATION_COLOR = 1150; //500 (kůže) a 1150 (kosti)
    final int SKULL_DEPTH = ImageProcessing.NUM_OF_IMAGES;
    final int SKULL_HEIGHT = ImageProcessing.FINAL_IMG_HEIGHT;
    final int SKULL_WIDTH = ImageProcessing.FINAL_IMG_WIDTH;
     
    int[][][] new_images = new int[SKULL_DEPTH][SKULL_WIDTH][SKULL_HEIGHT];   
    
    // rezy lebky
    BufferedImage[] images;
    
// aktualni Voxel - souradnice x,y,z  pro vrchol 0 + 8 okolnich barev
/*
4 x---------x 5
  |\         \
  | \         \
  |  \         \
  | 7 x---------x 6
  |   |         | 
0 x   |     x 1 |
   \  |         |
    \ |         |
     \|         |
    3 x---------x 2             
*/
    
  Vector3[] actVoxelVertices;// pozice vrcholu aktualniho voxelu
  int[] actVoxelColors = new int[8]; // aktualni barvy voxelu
  
  Vector3[] actTHVertices = new Vector3[4];
  int[] actTHColors = new int[4]; // aktualni barvy vrcholu tetrahedronu
  
  // Gradienty tetrahedronu
  Vector3[] gTetrahedron = new Vector3[4];
  
  // myslim, ze pokud snizuji jen na 8 kombinaci, tak se mi u zrcadlovych obraci normala
  boolean oppositeNormal = false;
 
  // serazene v posloupnosti podle x, y, z 
  // tzn. ze zaciname s nejmensimi z pro vsechny x, y a po to zvysujeme z
  // podle obrazku: http://www.ics.uci.edu/~eppstein/projects/tetra/
  // indexy vrcholu ve voxelu, ktery tvori dohromady 6 tetrahedronu  
 /* final int[][] tetrahedrons = {
        {0, 5, 1, 3},
        {0, 4, 5, 3},
        {4, 5, 3, 7},
        {5, 3, 7, 6},
        {5, 3, 2, 6},
        {1, 5, 3, 2}
    };
  */
  final int[][] tetrahedrons = {
        {0,5,1,6},
        {0,1,2,6},
        {0,2,3,6},
        {0,3,7,6},
        {0,7,4,6},
        {0,4,5,6}
  };
  
  
  //seznam trojuhelniku
  Vector<Triangle> list = new Vector<Triangle>(); 
    

    public VoxelProcessing(BufferedImage[] _images) {
        images = _images; 
        actVoxelVertices =  new Vector3[8];
        for (int i = 0; i < actVoxelVertices.length; i++) {
            actVoxelVertices[i] = new Vector3();
        }
        list.clear();
        for(int i = 0; i<8; i++)
            actVoxelVertices[i] = new Vector3();
   }   
    
    private void preprocessImages(BufferedImage[] images){
        System.out.println("Image preprocessing started ...");
        for(int z = 0; z<SKULL_DEPTH-1; z++){
            for(int x = 0; x<SKULL_WIDTH-1; x++){
                for(int y = 0; y<SKULL_HEIGHT-1; y++){    
                    new_images[z][x][y] = images[z].getRaster().getSample(x, y, 0);
                }    
            }
        }
        System.out.println("Image preprocessing finished");
    }
    
    
    // hledáme pozici x,y,z když známe tři barvy a dva vrcholy
    private Vector3 interpolatePosition(Vector3 vertex1, int color1, Vector3 vertex2, int color2){
        Vector3 ret = new Vector3();
        //zjistím si, kolik hodnot je mezi krajníma bodama
        float totalColours = color2-color1;  
        if(totalColours == 0){
            ret.xyz[0] = (vertex1.xyz[0]+vertex2.xyz[0])/2.0;
            ret.xyz[1] = (vertex1.xyz[1]+vertex2.xyz[1])/2.0;
            ret.xyz[2] = (vertex1.xyz[2]+vertex2.xyz[2])/2.0;
            return ret;
        }
        
        ret.xyz[0] = vertex1.x()+(vertex2.x()-vertex1.x())*(SEGMENATION_COLOR - color1)/totalColours;
        ret.xyz[1] = vertex1.y()+(vertex2.y()-vertex1.y())*(SEGMENATION_COLOR - color1)/totalColours;
        ret.xyz[2] = vertex1.z()+(vertex2.z()-vertex1.z())*(SEGMENATION_COLOR - color1)/totalColours;
        
        return ret;
    }
    
    public Vector3 computeGradient(int x, int y, int z){
        Vector3 gradient = new Vector3();
        //souradnice musi byt minimalne o jedno od hranice lebky (mozna to bude zde padat)
        if(x == 0 || x == SKULL_WIDTH -1) gradient.xyz[0] = new_images[z][x][y];
        else
            gradient.xyz[0] = images[z].getRaster().getSample(x-1, y, 0) -
                              images[z].getRaster().getSample(x+1, y, 0);
        
        if(y == 0 || y == SKULL_WIDTH -1) gradient.xyz[1] = new_images[z][x][y];
        else
            gradient.xyz[1] = new_images[z][x][y-1] - new_images[z][x][y+1];
        
        if(z == 0 || z == SKULL_DEPTH -1) gradient.xyz[2] = new_images[z][x][y];
        else 
        gradient.xyz[2] = new_images[z-1][x][y] - new_images[z+1][x][y];
        
        return gradient;
    }
    
    public Vector<Triangle> getTriangleList(){
        return list;
    }
    
  /* ohodnoceni vrcholu v urcitem tetrahedronu
       0
      /|\
     / | \
    /  |  \
   / _ | _ \ 
 3 \   |   / 1
    \  |  /
     \ | /
      \|/
       2
  
  */
    
   // 7 ruznych pripadu pri Marching tetrahedrons
    void case1OR14(){
         computeTHGradient();
         
      Triangle t = new Triangle();
      t.vertices[0] = interpolatePosition(actTHVertices[0], actTHColors[0], actTHVertices[3], actTHColors[3]);
      t.normals[0]  = interpolatePosition(gTetrahedron[0], actTHColors[0], gTetrahedron[3], actTHColors[3]); 
      t.vertices[1] = interpolatePosition(actTHVertices[0], actTHColors[0], actTHVertices[2], actTHColors[2]);
      t.normals[1]  = interpolatePosition(gTetrahedron[0], actTHColors[0], gTetrahedron[2], actTHColors[2]); 
      t.vertices[2] = interpolatePosition(actTHVertices[0], actTHColors[0], actTHVertices[1], actTHColors[1]);
      t.normals[2]  = interpolatePosition(gTetrahedron[0], actTHColors[0], gTetrahedron[1], actTHColors[1]); 
      
      //normalizace normal
      t.normals[0] = t.normals[0].normalize();
      t.normals[1] = t.normals[1].normalize();
      t.normals[2] = t.normals[2].normalize();
    
      list.add(t);
    }
    
    void case2OR13(){
         computeTHGradient();
         
      Triangle t = new Triangle();
      t.vertices[0] = interpolatePosition(actTHVertices[1], actTHColors[1], actTHVertices[0], actTHColors[0]);
      t.normals[0]  = interpolatePosition(gTetrahedron[1], actTHColors[1], gTetrahedron[0], actTHColors[0]); 
      t.vertices[1] = interpolatePosition(actTHVertices[1], actTHColors[1], actTHVertices[3], actTHColors[3]);
      t.normals[1]  = interpolatePosition(gTetrahedron[1], actTHColors[1], gTetrahedron[3], actTHColors[3]); 
      t.vertices[2] = interpolatePosition(actTHVertices[1], actTHColors[1], actTHVertices[2], actTHColors[2]);
      t.normals[2]  = interpolatePosition(gTetrahedron[1], actTHColors[1], gTetrahedron[2], actTHColors[2]); 
      
      //normalizace normal
      t.normals[0] = t.normals[0].normalize();
      t.normals[1] = t.normals[1].normalize();
      t.normals[2] = t.normals[2].normalize();
     
      list.add(t);
    }
    
    void case4OR11(){
         computeTHGradient();
         
      Triangle t = new Triangle();
      t.vertices[0] = interpolatePosition(actTHVertices[2], actTHColors[2], actTHVertices[1], actTHColors[1]);
      t.normals[0]  = interpolatePosition(gTetrahedron[2], actTHColors[2], gTetrahedron[1], actTHColors[1]); 
      t.vertices[1] = interpolatePosition(actTHVertices[2], actTHColors[2], actTHVertices[0], actTHColors[0]);
      t.normals[1]  = interpolatePosition(gTetrahedron[2], actTHColors[2], gTetrahedron[0], actTHColors[0]); 
      t.vertices[2] = interpolatePosition(actTHVertices[2], actTHColors[2], actTHVertices[3], actTHColors[3]);
      t.normals[2]  = interpolatePosition(gTetrahedron[2], actTHColors[2], gTetrahedron[3], actTHColors[3]); 
      
      //normalizace normal
      t.normals[0] = t.normals[0].normalize();
      t.normals[1] = t.normals[1].normalize();
      t.normals[2] = t.normals[2].normalize();
    
      list.add(t);
    }
    
    void case8OR7(){
       computeTHGradient();
        
      Triangle t = new Triangle();
      t.vertices[0] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[2], actTHColors[2]);
      t.normals[0]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[2], actTHColors[2]); 
      t.vertices[1] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[1], actTHColors[1]);
      t.normals[1]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[1], actTHColors[1]); 
      t.vertices[2] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[0], actTHColors[0]);
      t.normals[2]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[0], actTHColors[0]); 
      
      //normalizace normal
      t.normals[0] = t.normals[0].normalize();
      t.normals[1] = t.normals[1].normalize();
      t.normals[2] = t.normals[2].normalize();
    
      list.add(t);
    }
    
    void case3OR12(){
         computeTHGradient();
         
        //prvni trojuhelnik
        Triangle t1 = new Triangle();
        t1.vertices[0] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[1], actTHColors[1]);
        t1.normals[0]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[1], actTHColors[1]); 
        t1.vertices[1] = interpolatePosition(actTHVertices[2], actTHColors[2], actTHVertices[1], actTHColors[1]);
        t1.normals[1]  = interpolatePosition(gTetrahedron[2], actTHColors[2], gTetrahedron[1], actTHColors[1]); 
        t1.vertices[2] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[0], actTHColors[0]);
        t1.normals[2]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[0], actTHColors[0]); 
        //normalizace normal
        t1.normals[0] = t1.normals[0].normalize();
        t1.normals[1] = t1.normals[1].normalize();
        t1.normals[2] = t1.normals[2].normalize();
        list.add(t1);
        
        //druhy trojuhelnik - ma dva spolecne vrcholy s tim prvnim
        Triangle t2 = new Triangle();
        t2.vertices[0] = t1.vertices[2];
        t2.normals[0]  = t1.normals[2]; 
        t2.vertices[1] = t1.vertices[1];
        t2.normals[1]  = t1.normals[1];
        t2.vertices[2] = interpolatePosition(actTHVertices[0], actTHColors[0], actTHVertices[2], actTHColors[2]);
        t2.normals[2]  = interpolatePosition(gTetrahedron[0], actTHColors[0], gTetrahedron[2], actTHColors[2]); 
        //normalizace normal
        t2.normals[2] = t2.normals[2].normalize();
        list.add(t2);
    }
    
    void case5OR10(){
         computeTHGradient();
         
        //prvni trojuhelnik
        Triangle t1 = new Triangle();
        t1.vertices[0] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[2], actTHColors[2]);
        t1.normals[0]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[2], actTHColors[2]); 
        t1.vertices[1] = interpolatePosition(actTHVertices[2], actTHColors[2], actTHVertices[1], actTHColors[1]);
        t1.normals[1]  = interpolatePosition(gTetrahedron[2], actTHColors[2], gTetrahedron[1], actTHColors[1]); 
        t1.vertices[2] = interpolatePosition(actTHVertices[0], actTHColors[0], actTHVertices[3], actTHColors[3]);
        t1.normals[2]  = interpolatePosition(gTetrahedron[0], actTHColors[0], gTetrahedron[3], actTHColors[3]); 
        //normalizace normal
        t1.normals[0] = t1.normals[0].normalize();
        t1.normals[1] = t1.normals[1].normalize();
        t1.normals[2] = t1.normals[2].normalize();
        list.add(t1);

        //druhy trojuhelnik - ma dva spolecne vrcholy s tim prvnim
        Triangle t2 = new Triangle();
        t2.vertices[0] = t1.vertices[1];
        t2.normals[0]  = t1.normals[1];
        t2.vertices[1] = interpolatePosition(actTHVertices[1], actTHColors[1], actTHVertices[0], actTHColors[0]);
        t2.normals[1]  = interpolatePosition(gTetrahedron[1], actTHColors[1], gTetrahedron[0], actTHColors[0]); 
        t2.vertices[2] = t1.vertices[2];
        t2.normals[2]  = t1.normals[2];
        //normalizace normal
        t2.normals[1] = t2.normals[1].normalize();
        list.add(t2);
    }
    
    void case6OR9(){
        computeTHGradient();
        
        //prvni trojuhelnik
        Triangle t1 = new Triangle();
        t1.vertices[0] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[2], actTHColors[2]);
        t1.normals[0]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[2], actTHColors[2]); 
        t1.vertices[1] = interpolatePosition(actTHVertices[3], actTHColors[3], actTHVertices[1], actTHColors[1]);
        t1.normals[1]  = interpolatePosition(gTetrahedron[3], actTHColors[3], gTetrahedron[1], actTHColors[1]); 
        t1.vertices[2] = interpolatePosition(actTHVertices[0], actTHColors[0], actTHVertices[2], actTHColors[2]);
        t1.normals[2]  = interpolatePosition(gTetrahedron[0], actTHColors[0], gTetrahedron[2], actTHColors[2]); 
        //normalizace normal
        t1.normals[0] = t1.normals[0].normalize();
        t1.normals[1] = t1.normals[1].normalize();
        t1.normals[2] = t1.normals[2].normalize();
        list.add(t1);

        //druhy trojuhelnik - ma dva spolecne vrcholy s tim prvnim
        Triangle t2 = new Triangle();
        t2.vertices[0] = t1.vertices[1];
        t2.normals[0]  = t1.normals[1];
        t2.vertices[1] = interpolatePosition(actTHVertices[1], actTHColors[1], actTHVertices[0], actTHColors[0]);
        t2.normals[1]  = interpolatePosition(gTetrahedron[1], actTHColors[1], gTetrahedron[0], actTHColors[0]); 
        t2.vertices[2] = t1.vertices[2];
        t2.normals[2]  = t1.normals[2];
        //normalizace normal
        t2.normals[1] = t2.normals[1].normalize();
        list.add(t2);
    }
    
    void computeTHGradient(){
      //prirazeni gradientu pro aktualni tetrahedron
       gTetrahedron[0] = computeGradient((int)actTHVertices[0].x(), 
               (int)actTHVertices[0].y(), (int)actTHVertices[0].z());
       gTetrahedron[1] = computeGradient((int)actTHVertices[1].x(), 
               (int)actTHVertices[1].y(), (int)actTHVertices[1].z());
       gTetrahedron[2] = computeGradient((int)actTHVertices[2].x(), 
               (int)actTHVertices[2].y(), (int)actTHVertices[2].z());
       gTetrahedron[3] = computeGradient((int)actTHVertices[3].x(), 
               (int)actTHVertices[3].y(), (int)actTHVertices[3].z());
    }

   void computeTriangles(int[] indices){
       
       //prirazeni vrcholu pro aktualni tetrahedron
       actTHVertices[0] = actVoxelVertices[indices[0]];
       actTHVertices[1] = actVoxelVertices[indices[1]];
       actTHVertices[2] = actVoxelVertices[indices[2]];
       actTHVertices[3] = actVoxelVertices[indices[3]];
       
       //prirazeni barev pro aktualni tetrahedron
       actTHColors[0] = actVoxelColors[indices[0]];
       actTHColors[1] = actVoxelColors[indices[1]];
       actTHColors[2] = actVoxelColors[indices[2]];
       actTHColors[3] = actVoxelColors[indices[3]];
       
     //  System.out.println("actTHColors[0]: "+ actTHColors[0] + " actTHColors[1]: "+ actTHColors[1] +
     //          "actTHColors[2]: "+ actTHColors[2]);
       
       oppositeNormal = false;
       
    // urceni kombinace 
       
    // 1. obrazek - vlevo nahore
       if(actVoxelColors[indices[0]]<= SEGMENATION_COLOR && 
           SEGMENATION_COLOR <= actVoxelColors[indices[1]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[2]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[3]]){
           case1OR14();
       }
       else if(actVoxelColors[indices[0]]>= SEGMENATION_COLOR && 
           SEGMENATION_COLOR >= actVoxelColors[indices[1]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[2]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[3]])     
       { 
           oppositeNormal = true; 
           case1OR14();
       }
    // 2. obrazek - vpravo nahore 
      else if(actVoxelColors[indices[1]]<= SEGMENATION_COLOR && 
           SEGMENATION_COLOR <= actVoxelColors[indices[0]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[2]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[3]]){
           case2OR13();
      }     
      else if(actVoxelColors[indices[1]] >= SEGMENATION_COLOR && 
           SEGMENATION_COLOR >= actVoxelColors[indices[0]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[2]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[3]]){
           oppositeNormal = true;
           case2OR13();
      } 
    // 3. obrazek - uprostred  
      else if(actVoxelColors[indices[2]]<= SEGMENATION_COLOR && 
           SEGMENATION_COLOR <= actVoxelColors[indices[0]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[1]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[3]]){
          case4OR11();
      }     
      else if(actVoxelColors[indices[2]]>= SEGMENATION_COLOR && 
           SEGMENATION_COLOR >= actVoxelColors[indices[0]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[1]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[3]]){
          oppositeNormal = true;  
          case4OR11();
      }
      
   // 4. obrazek - uprostred vpravo   
      else if(actVoxelColors[indices[2]]<= SEGMENATION_COLOR && 
           SEGMENATION_COLOR <= actVoxelColors[indices[0]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[1]]&&
           SEGMENATION_COLOR <= actVoxelColors[indices[3]]){
          case8OR7();
      }     
      else if(actVoxelColors[indices[2]]>= SEGMENATION_COLOR && 
           SEGMENATION_COLOR >= actVoxelColors[indices[0]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[1]]&&
           SEGMENATION_COLOR >= actVoxelColors[indices[3]]){
          oppositeNormal = true;
          case8OR7();
      }     
    // 5. obrazek - dole vlevo   
       else if(actVoxelColors[indices[0]]<= SEGMENATION_COLOR &&
          actVoxelColors[indices[1]]<= SEGMENATION_COLOR &&
          SEGMENATION_COLOR <= actVoxelColors[indices[2]]&&
          SEGMENATION_COLOR <= actVoxelColors[indices[3]])
       {
          case3OR12();
       }
       
       else if(actVoxelColors[indices[0]]>= SEGMENATION_COLOR &&
          actVoxelColors[indices[1]]>= SEGMENATION_COLOR &&
          SEGMENATION_COLOR >= actVoxelColors[indices[2]]&&
          SEGMENATION_COLOR >= actVoxelColors[indices[3]])
       {
          oppositeNormal = true;
          case3OR12();
       }    
    // 6. obrazek - dole uprostred
       else if(actVoxelColors[indices[0]]<= SEGMENATION_COLOR &&
          actVoxelColors[indices[2]]<= SEGMENATION_COLOR &&
          SEGMENATION_COLOR <= actVoxelColors[indices[1]]&&
          SEGMENATION_COLOR <= actVoxelColors[indices[3]])
       {
          case5OR10();
       }
       
       else if(actVoxelColors[indices[0]] >= SEGMENATION_COLOR &&
          actVoxelColors[indices[2]]>= SEGMENATION_COLOR &&
          SEGMENATION_COLOR >= actVoxelColors[indices[1]]&&
          SEGMENATION_COLOR >= actVoxelColors[indices[3]])
       {
          oppositeNormal = true;
          case5OR10();
       }
     // 7. obrazek - dole vpravo  
       else if(actVoxelColors[indices[1]]<= SEGMENATION_COLOR &&
          actVoxelColors[indices[2]]<= SEGMENATION_COLOR &&
          SEGMENATION_COLOR <= actVoxelColors[indices[0]]&&
          SEGMENATION_COLOR <= actVoxelColors[indices[3]])
       {
          oppositeNormal = true;
          case6OR9();
       }
       else if(actVoxelColors[indices[1]] >= SEGMENATION_COLOR &&
          actVoxelColors[indices[2]]>= SEGMENATION_COLOR &&
          SEGMENATION_COLOR >= actVoxelColors[indices[0]]&&
          SEGMENATION_COLOR >= actVoxelColors[indices[3]])
       {
          case6OR9();
       }
       
       
   }
    
    
    public void marchingTetrahedrons(int x, int y, int z){
       //init voxel colors
       actVoxelVertices[0].setXYZ(x, y, z);
       actVoxelVertices[1].setXYZ(x+1, y, z);
       actVoxelVertices[2].setXYZ(x+1, y, z+1);
       actVoxelVertices[3].setXYZ(x, y, z+1);
       actVoxelVertices[4].setXYZ(x, y+1, z);
       actVoxelVertices[5].setXYZ(x+1, y+1, z);
       actVoxelVertices[6].setXYZ(x+1, y+1, z+1);
       actVoxelVertices[7].setXYZ(x, y+1, z+1);
      
       actVoxelColors[0] = new_images[z][x][y];
       actVoxelColors[1] = new_images[z][x+1][y];
       actVoxelColors[2] = new_images[z+1][x+1][y];
       actVoxelColors[3] = new_images[z+1][x][y];
       actVoxelColors[4] = new_images[z][x][y+1];
       actVoxelColors[5] = new_images[z][x+1][y+1];
       actVoxelColors[6] = new_images[z+1][x+1][y+1];
       actVoxelColors[7] = new_images[z+1][x][y+1];
       
       // pro kazdy tetrahedron v danem voxelu
       for(int t = 0; t<6; t++){
          computeTriangles(tetrahedrons[t]);
       }
    
    }
    
    public void marchingCubes(){
        preprocessImages(images);
        System.out.println("Marching tetrahedra started ...");
        for(int z = 0; z<SKULL_DEPTH -1; z++){
            for(int x = 0; x<SKULL_WIDTH -1; x++){
                for(int y = 0; y<SKULL_HEIGHT -1; y++){   
                    marchingTetrahedrons(x, y, z);
                }    
            }
        }
        System.out.println("Marching tetrahedra finished");
        System.out.println("Number of triangles: "+list.size());
    }
}
